home *** CD-ROM | disk | FTP | other *** search
/ Aminet 2 / Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso / Aminet / util / gnu / groff_src.lha / Groff-1.07 / groff / groff.cc next >
C/C++ Source or Header  |  1992-10-02  |  14KB  |  589 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.com)
  4.  
  5. This file is part of groff.
  6.  
  7. groff is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 2, or (at your option) any later
  10. version.
  11.  
  12. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  13. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file COPYING.  If not, write to the Free Software
  19. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  20.  
  21. // A front end for groff.
  22.  
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <stdlib.h>
  26. #include <signal.h>
  27. #include <errno.h>
  28.  
  29. #include "lib.h"
  30. #include "assert.h"
  31. #include "errarg.h"
  32. #include "error.h"
  33. #include "stringclass.h"
  34. #include "cset.h"
  35. #include "font.h"
  36. #include "device.h"
  37. #include "pipeline.h"
  38. #include "defs.h"
  39.  
  40. #define BSHELL "/bin/sh"
  41. #define GXDITVIEW "gxditview"
  42.  
  43. // troff will be passed an argument of -rXREG=1 if the -X option is
  44. // specified
  45. #define XREG ".X"
  46.  
  47. #ifndef STDLIB_H_DECLARES_PUTENV
  48. extern "C" {
  49.   int putenv(const char *);
  50. }
  51. #endif /* not STDLIB_H_DECLARES_PUTENV */
  52.  
  53. const char *strsignal(int);
  54.  
  55. const int SOELIM_INDEX = 0;
  56. const int REFER_INDEX = SOELIM_INDEX + 1;
  57. const int PIC_INDEX = REFER_INDEX + 1;
  58. const int TBL_INDEX = PIC_INDEX + 1;
  59. const int EQN_INDEX = TBL_INDEX + 1;
  60. const int TROFF_INDEX = EQN_INDEX + 1;
  61. const int POST_INDEX = TROFF_INDEX + 1;
  62. const int SPOOL_INDEX = POST_INDEX + 1;
  63.  
  64. const int NCOMMANDS = SPOOL_INDEX + 1;
  65.  
  66. class possible_command {
  67.   char *name;
  68.   string args;
  69.   char **argv;
  70.  
  71.   void build_argv();
  72. public:
  73.   possible_command();
  74.   ~possible_command();
  75.   void set_name(const char *);
  76.   void set_name(const char *, const char *);
  77.   const char *get_name();
  78.   void append_arg(const char *, const char * = 0);
  79.   void clear_args();
  80.   char **get_argv();
  81.   void print(int is_last, FILE *fp);
  82. };
  83.  
  84. int lflag = 0;
  85. char *spooler = 0;
  86. char *driver = 0;
  87.  
  88. possible_command commands[NCOMMANDS];
  89.  
  90. int run_commands();
  91. void print_commands();
  92. void append_arg_to_string(const char *arg, string &str);
  93. void handle_unknown_desc_command(const char *command, const char *arg,
  94.                  const char *filename, int lineno);
  95. const char *basename(const char *);
  96.  
  97. void usage();
  98. void help();
  99.  
  100. int main(int argc, char **argv)
  101. {
  102.   program_name = argv[0];
  103.   static char stderr_buf[BUFSIZ];
  104.   setbuf(stderr, stderr_buf);
  105.   assert(NCOMMANDS <= MAX_COMMANDS);
  106.   string Pargs, Largs, Fargs;
  107.   int Vflag = 0;
  108.   int zflag = 0;
  109.   int iflag = 0;
  110.   int Xflag = 0;
  111.   int opt;
  112.   const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
  113.   if (!command_prefix)
  114.     command_prefix = PROG_PREFIX;
  115.   commands[TROFF_INDEX].set_name(command_prefix, "troff");
  116.   while ((opt = getopt(argc, argv,
  117.                "itpeRszavVhblCENXZF:m:T:f:w:W:M:d:r:n:o:P:L:"))
  118.      != EOF) {
  119.     char buf[3];
  120.     buf[0] = '-';
  121.     buf[1] = opt;
  122.     buf[2] = '\0';
  123.     switch (opt) {
  124.     case 'i':
  125.       iflag = 1;
  126.       break;
  127.     case 't':
  128.       commands[TBL_INDEX].set_name(command_prefix, "tbl");
  129.       break;
  130.     case 'p':
  131.       commands[PIC_INDEX].set_name(command_prefix, "pic");
  132.       break;
  133.     case 'e':
  134.       commands[EQN_INDEX].set_name(command_prefix, "eqn");
  135.       break;
  136.     case 's':
  137.       commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
  138.       break;
  139.     case 'R':
  140.       commands[REFER_INDEX].set_name(command_prefix, "refer");
  141.       break;
  142.     case 'z':
  143.     case 'a':
  144.       commands[TROFF_INDEX].append_arg(buf);
  145.       // fall through
  146.     case 'Z':
  147.       zflag++;
  148.       break;
  149.     case 'l':
  150.       lflag++;
  151.       break;
  152.     case 'V':
  153.       Vflag++;
  154.       break;
  155.     case 'v':
  156.     case 'C':
  157.       commands[SOELIM_INDEX].append_arg(buf);
  158.       commands[PIC_INDEX].append_arg(buf);
  159.       commands[TBL_INDEX].append_arg(buf);
  160.       commands[EQN_INDEX].append_arg(buf);
  161.       commands[TROFF_INDEX].append_arg(buf);
  162.       break;
  163.     case 'N':
  164.       commands[EQN_INDEX].append_arg(buf);
  165.       break;
  166.     case 'h':
  167.       help();
  168.       break;
  169.     case 'E':
  170.     case 'b':
  171.       commands[TROFF_INDEX].append_arg(buf);
  172.       break;
  173.     case 'T':
  174.       if (strcmp(optarg, "Xps") == 0) {
  175.     warning("-TXps option is obsolete: use -X -Tps instead");
  176.     device = "ps";
  177.     Xflag++;
  178.       }
  179.       else
  180.     device = optarg;
  181.       break;
  182.     case 'F':
  183.       font::command_line_font_dir(optarg);
  184.       if (Fargs.length() > 0) {
  185.     Fargs += ':';
  186.     Fargs += optarg;
  187.       }
  188.       else
  189.     Fargs = optarg;
  190.       break;
  191.     case 'f':
  192.     case 'o':
  193.     case 'm':
  194.     case 'r':
  195.     case 'd':
  196.     case 'n':
  197.     case 'w':
  198.     case 'W':
  199.       commands[TROFF_INDEX].append_arg(buf, optarg);
  200.       break;
  201.     case 'M':
  202.       commands[EQN_INDEX].append_arg(buf, optarg);
  203.       commands[TROFF_INDEX].append_arg(buf, optarg);
  204.       break;
  205.     case 'P':
  206.       Pargs += optarg;
  207.       Pargs += '\0';
  208.       break;
  209.     case 'L':
  210.       append_arg_to_string(optarg, Largs);
  211.       break;
  212.     case 'X':
  213.       Xflag++;
  214.       break;
  215.     case '?':
  216.       usage();
  217.       break;
  218.     default:
  219.       assert(0);
  220.       break;
  221.     }
  222.   }
  223.   font::set_unknown_desc_command_handler(handle_unknown_desc_command);
  224.   if (!font::load_desc())
  225.     fatal("invalid device `%1'", device);
  226.   if (!driver)
  227.     fatal("no `postpro' command in DESC file for device `%1'", device);
  228.   const char *real_driver = 0;
  229.   if (Xflag) {
  230.     real_driver = driver;
  231.     driver = GXDITVIEW;
  232.     commands[TROFF_INDEX].append_arg("-r" XREG "=", "1");
  233.   }
  234.   if (driver)
  235.     commands[POST_INDEX].set_name(driver);
  236.   int gxditview_flag = driver && strcmp(basename(driver), GXDITVIEW) == 0;
  237.   if (gxditview_flag && argc - optind == 1) {
  238.     commands[POST_INDEX].append_arg("-title");
  239.     commands[POST_INDEX].append_arg(argv[optind]);
  240.     commands[POST_INDEX].append_arg("-xrm");
  241.     commands[POST_INDEX].append_arg("*iconName:", argv[optind]);
  242.     string filename_string("|");
  243.     append_arg_to_string(argv[0], filename_string);
  244.     append_arg_to_string("-Z", filename_string);
  245.     for (int i = 1; i < argc; i++)
  246.       append_arg_to_string(argv[i], filename_string);
  247.     filename_string += '\0';
  248.     commands[POST_INDEX].append_arg("-filename");
  249.     commands[POST_INDEX].append_arg(filename_string.contents());
  250.   }
  251.   if (gxditview_flag && Xflag) {
  252.     string print_string(real_driver);
  253.     if (spooler) {
  254.       print_string += " | ";
  255.       print_string += spooler;
  256.       print_string += Largs;
  257.     }
  258.     print_string += '\0';
  259.     commands[POST_INDEX].append_arg("-printCommand");
  260.     commands[POST_INDEX].append_arg(print_string.contents());
  261.   }
  262.   const char *p = Pargs.contents();
  263.   const char *end = p + Pargs.length();
  264.   while (p < end) {
  265.     commands[POST_INDEX].append_arg(p);
  266.     p = strchr(p, '\0') + 1;
  267.   }
  268.   if (gxditview_flag)
  269.     commands[POST_INDEX].append_arg("-");
  270.   if (lflag && !Xflag && spooler) {
  271.     commands[SPOOL_INDEX].set_name(BSHELL);
  272.     commands[SPOOL_INDEX].append_arg("-c");
  273.     Largs += '\0';
  274.     Largs = spooler + Largs;
  275.     commands[SPOOL_INDEX].append_arg(Largs.contents());
  276.   }
  277.   if (zflag) {
  278.     commands[POST_INDEX].set_name(0);
  279.     commands[SPOOL_INDEX].set_name(0);
  280.   }
  281.   commands[TROFF_INDEX].append_arg("-T", device);
  282.   commands[EQN_INDEX].append_arg("-T", device);
  283.  
  284.   for (int first_index = 0; first_index < TROFF_INDEX; first_index++)
  285.     if (commands[first_index].get_name() != 0)
  286.       break;
  287.   if (optind < argc) {
  288.     if (argv[optind][0] == '-' && argv[optind][1] != '\0')
  289.       commands[first_index].append_arg("--");
  290.     for (int i = optind; i < argc; i++)
  291.       commands[first_index].append_arg(argv[i]);
  292.     if (iflag)
  293.       commands[first_index].append_arg("-");
  294.   }
  295.   if (Fargs.length() > 0) {
  296.     string e = "GROFF_FONT_PATH";
  297.     e += '=';
  298.     e += Fargs;
  299.     char *fontpath = getenv("GROFF_FONT_PATH");
  300.     if (fontpath && *fontpath) {
  301.       e += ':';
  302.       e += fontpath;
  303.     }
  304.     e += '\0';
  305.     if (putenv(strsave(e.contents())))
  306.       fatal("putenv failed");
  307.   }
  308.   if (Vflag) {
  309.     print_commands();
  310.     exit(0);
  311.   }
  312.   exit(run_commands());
  313. }
  314.  
  315. const char *basename(const char *s)
  316. {
  317.   if (!s)
  318.     return 0;
  319.   const char *p = strrchr(s, '/');
  320.   return p ? p + 1 : s;
  321. }
  322.  
  323. void handle_unknown_desc_command(const char *command, const char *arg,
  324.                  const char *filename, int lineno)
  325. {
  326.   if (strcmp(command, "print") == 0) {
  327.     if (arg == 0)
  328.       error_with_file_and_line(filename, lineno,
  329.                    "`print' command requires an argument");
  330.     else
  331.       spooler = strsave(arg);
  332.   }
  333.   if (strcmp(command, "postpro") == 0) {
  334.     if (arg == 0)
  335.       error_with_file_and_line(filename, lineno,
  336.                    "`postpro' command requires an argument");
  337.     else {
  338.       for (const char *p = arg; *p; p++)
  339.     if (csspace(*p)) {
  340.       error_with_file_and_line(filename, lineno,
  341.                    "invalid `postpro' argument `%1'"
  342.                    ": program name required", arg);
  343.       return;
  344.     }
  345.       driver = strsave(arg);
  346.     }
  347.   }
  348. }
  349.  
  350. void print_commands()
  351. {
  352.   for (int last = SPOOL_INDEX; last >= 0; last--)
  353.     if (commands[last].get_name() != 0)
  354.       break;
  355.   for (int i = 0; i <= last; i++)
  356.     if (commands[i].get_name() != 0)
  357.       commands[i].print(i == last, stdout);
  358. }
  359.  
  360. // Run the commands. Return the code with which to exit.
  361.  
  362. int run_commands()
  363. {
  364.   char **v[NCOMMANDS];
  365.   int j = 0;
  366.   for (int i = 0; i < NCOMMANDS; i++)
  367.     if (commands[i].get_name() != 0)
  368.       v[j++] = commands[i].get_argv();
  369.   return run_pipeline(j, v);
  370. }
  371.  
  372. possible_command::possible_command()
  373. : name(0), argv(0)
  374. {
  375. }
  376.  
  377. possible_command::~possible_command()
  378. {
  379.   delete name;
  380.   delete argv;
  381. }
  382.  
  383. void possible_command::set_name(const char *s)
  384. {
  385.   name = strsave(s);
  386. }
  387.  
  388. void possible_command::set_name(const char *s1, const char *s2)
  389. {
  390.   name = new char[strlen(s1) + strlen(s2) + 1];
  391.   strcpy(name, s1);
  392.   strcat(name, s2);
  393. }
  394.  
  395. const char *possible_command::get_name()
  396. {
  397.   return name;
  398. }
  399.  
  400. void possible_command::clear_args()
  401. {
  402.   args.clear();
  403. }
  404.  
  405. void possible_command::append_arg(const char *s, const char *t)
  406. {
  407.   args += s;
  408.   if (t)
  409.     args += t;
  410.   args += '\0';
  411. }
  412.  
  413. void possible_command::build_argv()
  414. {
  415.   if (argv)
  416.     return;
  417.   // Count the number of arguments.
  418.   int len = args.length();
  419.   int argc = 1;
  420.   char *p = 0;
  421.   if (len > 0) {
  422.     p = &args[0];
  423.     for (int i = 0; i < len; i++)
  424.       if (p[i] == '\0')
  425.     argc++;
  426.   }
  427.   // Build an argument vector.
  428.   argv = new char *[argc + 1];
  429.   argv[0] = name;
  430.   for (int i = 1; i < argc; i++) {
  431.     argv[i] = p;
  432.     p = strchr(p, '\0') + 1;
  433.   }
  434.   argv[argc] = 0;
  435. }
  436.  
  437. void possible_command::print(int is_last, FILE *fp)
  438. {
  439.   build_argv();
  440.   if (argv[0] != 0 && strcmp(argv[0], BSHELL) == 0
  441.       && argv[1] != 0 && strcmp(argv[1], "-c") == 0
  442.       && argv[2] != 0 && argv[3] == 0)
  443.     fputs(argv[2], fp);
  444.   else {
  445.     fputs(argv[0], fp);
  446.     string str;
  447.     for (int i = 1; argv[i] != 0; i++) {
  448.       str.clear();
  449.       append_arg_to_string(argv[i], str);
  450.       put_string(str, fp);
  451.     }
  452.   }
  453.   if (is_last)
  454.     putc('\n', fp);
  455.   else
  456.     fputs(" | ", fp);
  457. }
  458.  
  459. void append_arg_to_string(const char *arg, string &str)
  460. {
  461.   str += ' ';
  462.   int needs_quoting = 0;
  463.   int contains_single_quote = 0;
  464.   for (const char *p = arg; *p != '\0'; p++)
  465.     switch (*p) {
  466.     case ';':
  467.     case '&':
  468.     case '(':
  469.     case ')':
  470.     case '|':
  471.     case '^':
  472.     case '<':
  473.     case '>':
  474.     case '\n':
  475.     case ' ':
  476.     case '\t':
  477.     case '\\':
  478.     case '"':
  479.     case '$':
  480.     case '?':
  481.     case '*':
  482.       needs_quoting = 1;
  483.       break;
  484.     case '\'':
  485.       contains_single_quote = 1;
  486.       break;
  487.     }
  488.   if (contains_single_quote || arg[0] == '\0') {
  489.     str += '"';
  490.     for (p = arg; *p != '\0'; p++)
  491.       switch (*p) {
  492.       case '"':
  493.       case '\\':
  494.       case '$':
  495.     str += '\\';
  496.     // fall through
  497.       default:
  498.     str += *p;
  499.     break;
  500.       }
  501.     str += '"';
  502.   }
  503.   else if (needs_quoting) {
  504.     str += '\'';
  505.     str += arg;
  506.     str += '\'';
  507.   }
  508.   else
  509.     str += arg;
  510. }
  511.  
  512. char **possible_command::get_argv()
  513. {
  514.   build_argv();
  515.   return argv;
  516. }
  517.  
  518. void synopsis()
  519. {
  520.   fprintf(stderr,
  521. "usage: %s [-abehilpstvzCENRVXZ] [-Fdir] [-mname] [-Tdev] [-ffam] [-wname]\n"
  522. "       [-Wname] [ -Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg] [-Larg]\n"
  523. "       [files...]\n",
  524.       program_name);
  525. }
  526.  
  527. void help()
  528. {
  529.   synopsis();
  530.   fputs("\n"
  531. "-h\tprint this message\n"
  532. "-t\tpreprocess with tbl\n"
  533. "-p\tpreprocess with pic\n"
  534. "-e\tpreprocess with eqn\n"
  535. "-s\tpreprocess with soelim\n"
  536. "-R\tpreprocess with refer\n"
  537. "-Tdev\tuse device dev\n"
  538. "-X\tuse X11 previewer rather than usual postprocessor\n"
  539. "-mname\tread macros tmac.name\n"
  540. "-dcs\tdefine a string c as s\n"
  541. "-rcn\tdefine a number register c as n\n"
  542. "-nnum\tnumber first page n\n"
  543. "-olist\toutput only pages in list\n"
  544. "-ffam\tuse fam as the default font family\n"
  545. "-Fdir\tsearch directory dir for device directories\n"
  546. "-Mdir\tsearch dir for macro files\n"
  547. "-v\tprint version number\n"
  548. "-z\tsuppress formatted output\n"
  549. "-Z\tdon't postprocess\n"
  550. "-a\tproduce ASCII description of output\n"
  551. "-i\tread standard input after named input files\n"
  552. "-wname\tenable warning name\n"
  553. "-Wname\tinhibit warning name\n"
  554. "-E\tinhibit all errors\n"
  555. "-b\tprint backtraces with errors or warnings\n"
  556. "-l\tspool the output\n"
  557. "-C\tenable compatibility mode\n"
  558. "-V\tprint commands on stdout instead of running them\n"
  559. "-Parg\tpass arg to the postprocessor\n"
  560. "-Larg\tpass arg to the spooler\n"
  561. "-N\tdon't allow newlines within eqn delimiters\n"
  562. "\n",
  563.     stderr);
  564.   exit(0);
  565. }
  566.  
  567. void usage()
  568. {
  569.   synopsis();
  570.   fprintf(stderr, "%s -h gives more help\n", program_name);
  571.   exit(1);
  572. }
  573.  
  574. extern "C" {
  575.  
  576. void c_error(const char *format, const char *arg1, const char *arg2,
  577.          const char *arg3)
  578. {
  579.   error(format, arg1, arg2, arg3);
  580. }
  581.  
  582. void c_fatal(const char *format, const char *arg1, const char *arg2,
  583.          const char *arg3)
  584. {
  585.   fatal(format, arg1, arg2, arg3);
  586. }
  587.  
  588. }
  589.